package cn.lhjl.day09.note;
/*
* 并发：两个或多个事件在同一个时间段发生，Cpu在多个任务间交替执行
* 并行：两个或多个事件同时发生，多核cpu的不同核心执行不同任务。
*
* 进程：进入到内存中占有一部分空间执行的程序叫进程。每个进程都有一个独立的内存空间，一个应用程序可以同时运行
* 多个进程，
*
*
* 线程：线程是进程的一个执行单元，负责当前进程中程序的执行，一个进程至少有一个线程。一个进程中可以有
* 多个线程，这个应用程序也可以称为多线程程序。线程是程序执行时的最小单位，它是进程的一个执行流，是CPU调度和分派的基本单位，
* 一个进程可以由很多个线程组成，线程间共享进程的所有资源，每个线程有自己的堆栈和局部变量。
* 线程由CPU独立调度执行，在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作，每个请求分配一个线程来处理。
*
* 线程调度：
*  分时调度：所有线程轮流使用cpu的使用权，平均分配每个线程占用cpu的时间。
*  抢占式调度：优先让优先级高的线程的线程使用cpu，如果线程的优先级相同，那么会随机选择一个（线程随机性），
*  java使用抢占式调度。
*  注意：window10可以在任务管理器里设置优先级各个程序优先级。
*
* Jvm执行main方法，main方法会进入栈内存，JVM会找操作系统开辟一条main方法通向cpu的执行路径，
* cpu就可以通过这个路径执行main方法，而这个路径有一个名字，叫主线程。
*
* 创建多线程程序的第一种方式：创建Thread类的子类
* java.lang.Thread:是描述线程的类，我们想要实现多线程应用程序，就必须继承Thread类
*
* 实现步骤：
* 1.创建一个Thread类的子类
* 2.在这个子类中重写Thread类的run方法，设置线程任务（开启线程要做什么？）
* 3.创建Thread的子类对象。
* 4.用这个子类对象调用Thread类的start方法，开起多线程，执行run方法。
*   void start（）使该线程开始执行，java虚拟机调用该线程的run方法，如果是结果是两个线程并发运行；一个是main线程
* 一个是创建的新线程，多次启动一个线程是非法的，特别是当线程已经结束执行后，不能重写启动。
* 由于java虚拟机是抢占式调度，哪个优先级高，先执行哪个线程，优先级一样，则随机执行。
*
* start方法一执行，会开辟一个新的栈空间，这个空间与main方法的栈空间独立，cpu有了选择栈执行方法的权利。
* 这也是多线程的好处，在不同栈中执行所以互不影响。
*
* 获取线程名称：
* 1.使用Thread类的方法String getName（）。返回该线程的名称
* 2.可以先用Thread类的静态方法Thread currentThread（）获取当前正在执行的线程，然后使用线程中的方法
* getName（）获取当前正在执行线程的名称。
* 链式写法：Thread.currentThread.getName
*
* 设置多线程的名称：
* 1.使用Thread类中setName()方法
* 2.给MyThread创建了一个带参的构造方法
* public void MyThread（String name）{
*       super(name)
* }
*
* public static void sleep(long millis):使当前正在执行的线程以制定的毫秒数暂停（停止执行）。
* 好描述结束之后，线程继续执行。
*
* 创建多线程程序的第二种方式：实现Runnable接口。
* java.lang.Runnable
*   Runnable接口应该由那些打算通过某一线程来执行其实例的类来实现，类必须定义一个run的无参方法。
* java.lang.Thread类的构造方法
*   Thread(Runnable target)分配新的Thread对象。
*   Thread(Runnable target,String name)分派新的Thread对象，并给线程取名。
*
* 实现步骤：
* 1.创建一个Runnable接口的实现类。
* 2.在实现类中重写run方法，设置线程任务
* 3.创建一个Runnable接口实现对象
* 4.创建Thread对象，构造方法中传递参数为刚刚创建的Runnable接口实现类对象。
* 5.Thread对象调用start方法，开起新的线程执行run方法。
*
* 实现Runnable接口创建多线程的好处
* 1.避免了单继承的局限性。
*  一个类只能继承一个类，类继承了Thread类就不能继承其他类了，实现Runnable接口，还可以继承其他类，其他接口。
* 2.增强了程序的扩展性，降低了程序的耦合性。
*   实现Runnable接口的方法，把设置线程任务和开起新线程任务进行了分离（解耦）
*   实现类中，重写run方法，用来设置线程任务
*   创建Thread对象，调用start方法，用来开启新线程。
*
* 可以用匿名内部类的方式来实现多线程。
*
* 多线程访问了共享的数据，就会产生线程安全问题。
*
* 买票案例中，可能多个线程显示在卖同一张票，还可能卖到负数的票
* 原理：1.卖重复的票：多个线程都进入了if语句内，并且打印了当前的ticket数，但是除
* 都是打印完就失去了cpu执行权力，还没有执行ticket--语句。因此下一个线程执行的时候还是打印一个没有改变
* 的ticket值。*      2.卖出0票和负票：在票只有1张时，多个线程都通过了if语句，（因为即使已经有一个线程进入了if语句，但是也可能
* 因为他还没有执行ticket--就失去cpu使用权，导致之后的线程也能进入循环，然后第一个线程打印完ticket并ticket--后
* 第二个来线程会打印一个0再ticket--，第三个进入if的语句会打印-1，并ticket--。
*
* 这个案例告诉我们：多线程存在安全问题，我们需要解决方案
* 解决方案1：使用同步代码块：
*           格式：
*                   synchronized（锁对象）{
*                       可能出现线程安全问题的代码（访问了共享数据的代码）
*                   }
*
*注意：1.锁对象可以是任意对象
*     2.但是必须保证多个线程使用的锁对象是同一个。
*     3.锁对象作用：
*           把同步代码块锁住，只让一个线程在同步代码块中执行。
*
* 同步技术的原理：
* 使用了一锁对象，这个锁对象叫同步锁，也叫同步监视器。
* 3个线程一起抢夺cpu执行权，谁抢到了谁执行run方法进行买票。
* t0抢到了cpu的执行权，执行run方法，遇到了synchronized代码块，这时候t0会检查synchronized代码块是否
* 拥有锁对象，发现有，就会获取锁对象，进入同步代码块中执行语句。
*
* 当t1抢到cpu使用权的时候，执行run方法，遇到synchronized代码块，这时t1检查synchronized代码块是否拥有
* 有锁对象
* 发现没有，t1就会进入阻塞状态，会一直等待到t0线程归还锁对象，t0直到执行完同步中的代码，才会把锁对象
* 归还给同步代码块。这时t1才有可能进入同步代码块中（需在锁对象没有被再次拿走之前抢到锁对象）
*
* 解决方案2：使用同步方法：使用同步方法
* 使用步骤：
*   1.把访问了共享数据的代码从run（）方法中抽取出来，用来在RunnableImp中创建一个方法。
*   2.在方法上添加synchronized修饰符
* 格式：定义方法的格式
* 修饰符 synchronized 返回值 函数名（参数列表）{}
*
* 也就是说，定义一个同步方法也会把方法内部的代码锁住，只让一个线程执行，
* 这时候同步方法的锁对象是 实现类对象 new RunnableImpl（），也就是this
* 补充：使用静态同步方法
* 把普通方法变成静态方法，给方法加上static修饰符，并且给方法访问的成员变量加上static修饰符。
*
* 解决线程安全问题的第三种方案：使用lock锁
* java.utils.concurrent.locks.lock接口
* lock 提供了比使用 synchronized方法和语句更好的一种锁形式
* lock接口中的方法：
*   void lock()获得锁
*   void unlock()释放锁
* java.utils.concurrent.locks.ReentrantLock implements lock接口
* 使用步骤：
* 1.在成员变量创建一个ReentrantLock对象
* 2.在可能出现线程安全问题的代码前调用lock接口中的lock（）获取锁
* 3.在可能会出现安全问题的代码后调用lock接口中的unlock（）释放锁。
* 使用lock接口好处：
* 在try catch finally 中，可以把unlock写在finally语句块中，这样即使try中语句异常，也会在finally
* 中归还锁。
* 线程的六种状态：
* 1.新建状态：线程对象Thread被创建，但是还没调用start方法
* 2.就绪状态：线程对象被创建，也调用了start方法，此时线程位于可运行线程池中，等待获取cpu使用权。
* 3.运行状态：线程获得cpu使用权。执行程序代码。
* 4.锁阻塞状态：线程尝试获取某个同步块/同步方法的对象锁时，如果对象锁已经被其他线程取走，则
* 则线程进入锁阻塞状态，jvm把线程放在锁池中。
* 5.无线等待：一个线程在等待另外一个线程执行一个唤醒动作时，该线程进入Waiting状态，进入这个状态后是不能
* 自动唤醒的，必须等待另外一个线程调用notify或者notifyAll方法才能唤醒。
* 6.计时状态：调用sleep（）方法，程序进入计时状态，交出cpu使用权，并在一段设置的时间内不参与争抢cpu
* 使用权
* 7.死亡状态：线程执行完毕或者线程发生异常。
*
* 锁对象的方法wait()和notify().
* 在同步代码块中，用对象锁调用wait（）例如：obj.wait。会让执行到这个条语句的线程释放掉锁，其他线程重新来
* 竞争锁对象，当其他线程进入同步代码块执行到obj.notify时候，随机通知一个在wait的线程，这个线程一结束，在
* wait的线程立马获得锁 。
* 补充：1.wait有带参的重载方法，wait(long m)方法，wait方法如果在毫秒结束之后，还没有被唤醒，就会自动醒来
*
*       notifyAll是唤醒所有在等待这个对象锁的线程。
*
* 使用notify和wait要注意的细节：
* 1.wait方法与notify方法必须要由同一个锁对象调用，因为：对应的锁对象可以通过notify唤醒使用同一个锁
* 对象调用的wait方法后的线程
* 2.wait方法与notify方法是属于Object类的。因此锁对象可以是任意对象
* 3.wait与notify方法必须要在同步代码块或者同步函数中使用。因为：必须通过锁对象调用这两个方法。
* 线程间的通信： 以生产包子
*
*
* 死锁:有两个线程和两个锁对象才可能发生，即一个线程需要先获取A锁再获取B锁，一个线程需要先获取B锁再获取A锁
* 那么就可能在代码运行后，一个线程获得A锁，一个线程获得了B锁，此时两个线程都需要得到对方的那个锁，但是都不会主动
* 释放自己的锁，因此两个线程都阻塞，造成了死锁
*
* 解决方法：
* 1.两个线程的run方法都按某个相同的顺序获取锁。
* 2.给线程持有锁的时间设置上限，到达时间后自动释放，这个可以通过Lock类的trylock做到
* 3.用一些检测死锁的工具。
*
* 线程池原理：当程序第一次启动时，创建多个线程，把他们存放达到一个集合中，当有任务需要自行时，取出线程来使用，
* 用完再还回去
*
*
* 使用线程池的好处：
* 1.降低资源消耗，减少了创建线程和销毁线程的次数，每个线程都可以被重复利用，执行多个任务（线程池中的线程有执行完
* 给的任务后不会结束run方法）
* 2.提高了响应速度，任务到达时，任务不需要等到线程创建就能执行。
* 3.提高了线程的可管理性，可根据系统承受能力，调整线程池中线程的数量，（线程太多会导致死机）
*
* jdk1.5后提供的：
* java.util.concurrent.Executors:线程池的工厂类，用来生成线程池
* Executor类中的常用静态方法：
*   static ExecutorService newFixedThreadPool(int nThread)创建一个可重用的固定线程数的线程池
* 参数 int nThread线程池的固定线程数
* 返回值：ExecutorService接口，返回的是ExecutorService接口的实现类对象，我们可以使用ExecutorService接口来接收。
*
* java.util.concurrent.ExecutorService：线程池接口
* submit（Runnable task）提交一个任务
* void shutdown() 关闭线程池
* 使用步骤：1.用线程池的工程类Executors里的newFixedThreadPool生产一个指定数量的线程池，用ExecutorService接口接收
*          2.创建一个实现了Runnable接口的类，重写run方法，在其中设置线程任务
*          3.用ExecutorService对象调用submit方法，传递线程任务(Runnable的实现类)，开启线程，执行run方法
*          4.不使用线程池时用用shutdown将其销毁
*
*
*
* */
public class 多线程 {
}
